package server

import (
	"context"
	"github.com/pingcap-incubator/tinykv/kv/storage"
	"github.com/pingcap-incubator/tinykv/proto/pkg/kvrpcpb"
	"github.com/pingcap/log"
	"go.uber.org/zap"
)

// The functions below are Server's Raw API. (implements TinyKvServer).
// Some helper methods can be found in sever.go in the current directory

// RawGet return the corresponding Get response based on RawGetRequest's CF and Key fields
func (server *Server) RawGet(ctx context.Context, request *kvrpcpb.RawGetRequest) (*kvrpcpb.RawGetResponse, error) {
	// Your Code Here (1).
	response := &kvrpcpb.RawGetResponse{}

	reader, fail := server.storage.Reader(request.Context)
	if fail != nil {
		log.Error("fail rawGet in RawGetReader. reason=", zap.Error(fail))
		return nil, fail
	}
	// responseBytes无数据则未查询到，置NotFount字段
	responseBytes, _ := reader.GetCF(request.Cf, request.Key)
	if responseBytes == nil || len(responseBytes) == 0 {
		response.NotFound = true
	}

	response.Value = responseBytes
	return response, nil
}

// RawPut puts the target data into storage and returns the corresponding response
func (server *Server) RawPut(ctx context.Context, request *kvrpcpb.RawPutRequest) (*kvrpcpb.RawPutResponse, error) {
	// Your Code Here (1).
	// Hint: Consider using Storage.Modify to store data to be modified
	response := &kvrpcpb.RawPutResponse{}
	modifyPut := storage.Put{Cf: request.Cf, Key: request.Key, Value: request.Value}
	modify := storage.Modify{Data: modifyPut}
	modifies := []storage.Modify{modify}
	fail := server.storage.Write(nil, modifies)
	return response, fail
}

// RawDelete delete the target data from storage and returns the corresponding response
func (server *Server) RawDelete(_ context.Context, request *kvrpcpb.RawDeleteRequest) (*kvrpcpb.RawDeleteResponse, error) {
	// Your Code Here (1).
	// Hint: Consider using Storage.Modify to store data to be deleted
	response := &kvrpcpb.RawDeleteResponse{}
	modifyDelete := storage.Delete{Cf: request.Cf, Key: request.Key}
	modify := storage.Modify{Data: modifyDelete}
	modifies := []storage.Modify{modify}
	fail := server.storage.Write(nil, modifies)
	return response, fail
}

// RawScan scan the data starting from the start key up to limit. and return the corresponding result
func (server *Server) RawScan(_ context.Context, request *kvrpcpb.RawScanRequest) (*kvrpcpb.RawScanResponse, error) {
	// Your Code Here (1).
	// Hint: Consider using reader.IterCF
	response := &kvrpcpb.RawScanResponse{}

	reader, fail := server.storage.Reader(request.Context)
	if fail != nil {
		log.Error("fail rawGet in ScanGetReader. reason=", zap.Error(fail))
		return nil, fail
	}
	iterator := reader.IterCF(request.Cf)
	iterator.Seek(request.StartKey)

	var count uint32 = 0
	for ; iterator.Valid(); iterator.Next() {
		if request.Limit > 0 && count >= request.Limit {
			break
		}
		item := iterator.Item()
		key := item.KeyCopy(nil)
		value, fail := item.ValueCopy(nil)
		log.Debug("scanning k={}, v={}", zap.ByteString("key", key), zap.ByteString("value", value))
		if fail != nil {
			log.Error("fail rawGet in ScanGetValue. reason=", zap.Error(fail))
			return nil, fail
		}
		response.Kvs = append(response.Kvs, &kvrpcpb.KvPair{
			Key:   key,
			Value: value,
		})
		count++
	}
	return response, nil
}
